package com.swdegao.iot.netty.udp.server.channelhandler;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.fastjson.JSON;
import com.swdegao.iot.SwdegaoIotMgtServerApplication;
import com.swdegao.iot.netty.entity.HandlerMessage;
import com.swdegao.iot.netty.entity.ImeiInfo;
import com.swdegao.iot.netty.entity.OnOffData;
import com.swdegao.iot.netty.entity.Parameter;
import com.swdegao.iot.netty.entity.Terminals;
import com.swdegao.iot.netty.service.impl.TerminalsServiceImpl;
import com.swdegao.iot.netty.udp.server.NettyUDPServer;
import com.swdegao.iot.netty.utils.UDPConst;
import com.swdegao.iot.netty.utils.ByteUtils;
import com.swdegao.iot.netty.utils.ConvertUtils;
import com.swdegao.iot.netty.utils.DataResction;
import com.swdegao.iot.netty.utils.DataValida;
import com.swdegao.iot.netty.utils.RedisUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * @Author:zt
 * @Date: Created in 8:20 2018\10\26 0002
 */

// public class ParseMessgaeHandler extends
// SimpleChannelInboundHandler<DatagramPacket>{
public class ParseMessgaeHandler extends SimpleChannelInboundHandler<DatagramPacket> {
	// 定义一个全局的记录器，通过LoggerFactory获取
	private final static Logger logger = LoggerFactory.getLogger(ParseMessgaeHandler.class);

	@Autowired
	private RedisUtils redisUtils = SwdegaoIotMgtServerApplication.getBean(RedisUtils.class);
	static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

	private HandlerMessage hm = new HandlerMessage();
	@Autowired
	private TerminalsServiceImpl ts = SwdegaoIotMgtServerApplication.getBean(TerminalsServiceImpl.class);

	@SuppressWarnings("unused")
	private String tempTime = null;// 用于比较心跳是否超时
	private Boolean flag = true;

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { // (4)

		// Channel channel = ctx.channel();
		// logger.info("UDP服务器收到客户端消息");
		// DatagramPacket dp = (DatagramPacket)msg;
		ByteBuf in = msg.content();
		String clintdata=ByteBufUtil.hexDump(in).toUpperCase();
		logger.info(clintdata);
		byte[] barray = new byte[in.readableBytes()];
		in.getBytes(0, barray);
		// 将byte[]转成字符串用于打印
		// System.out.println(new String(barray));

		// ByteBuf in = (ByteBuf)msg;// 80 23 00 81
		// 383630313934303332393334363036 40 71 0d 0a
		/*
		 * byte[] b = ByteBufUtil.getBytes(in); System.out.println(new
		 * String(b));
		 */
		ByteBuf bodyAll = in.duplicate();

		// ByteBuf bodyAll = in;
		String ChannelID = null;
		try {
			if (DataValida.ValidateHead(in.getByte(0))) {// 验证帧头:0x80
				byte[] byteChannel = DataResction.ResctionIMEI(in);
				ChannelID = new String(byteChannel);
				// 更换连接ID
				if (!UDPConst.hasChannelID(ChannelID)) {
					String realChannelID = UDPConst.isChannel(ctx.channel());
					// logger.info("!UDPConst.hasChannelID,realChannelID "
					// +realChannelID);
					UDPConst.ChangeClientId(realChannelID, ChannelID);
					// 记录上线记录
					onoffLineAction(ChannelID, Parameter.online_des, 1);// 记录上线
					onoffLineForCenter(ChannelID, 1);
				}
				// 检查重复链接ID 不同实例 切换实例
				if (UDPConst.ischangeChannelID(ChannelID, ctx.channel())) {
					logger.info("UDPConst.ischangeChannelID,ChannelID " + ChannelID);
					UDPConst.changeChannel(ChannelID, ctx.channel());
					// 记录重复上线记录
					onoffLineAction(ChannelID, Parameter.reonline_des, 0);// 记录下线
					onoffLineForCenter(ChannelID, 0);
				}
				// 数据域
				ByteBuf recevieMsg = Unpooled.wrappedBuffer(DataResction.ResctionBody(bodyAll));
				// 数据类型:0x81,0x82,0x83,0x84
				Byte type = DataResction.ResctionDataType(in.getByte(3));
				hm.setActionTpye(type);
				hm.setImei(ChannelID);
				hm.setRecevieMsg(recevieMsg);
				// logger.info("UDP客户端IMEI:"+ ChannelID);
				// 走推送逻辑
				// Byte type = msg.getActionTpye();
				// 数据类型判断
				String Imei = ChannelID;
				switch (type) {
				case (byte) 0x81:
					// 注册包
					hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
					// ChannelFuture cf = ctx.writeAndFlush(hm.getSendMsg());
					ChannelFuture cf = ctx.writeAndFlush(new DatagramPacket(hm.getSendMsg(), msg.sender())).sync();
					cf.addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							if (future.isSuccess()) {
								logger.info(Imei+"0X81-1:回写成功");
							} else {
								logger.info(Imei+"0X81-1:回写失败");
							}
						}
					});
					ImeiInfo imeiInfo = JSON.parseObject(redisUtils.hmGet(Parameter.imei_info, ChannelID).toString(),
							ImeiInfo.class);
					int heartbeat_registered_num = imeiInfo.getHeartbeat_registered_num();
					cf.addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture arg0) throws Exception {
							// 检查操作的状态
							if (!cf.isSuccess()) {
								if (flag) {
									hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
									// ChannelFuture cfc =
									// ctx.writeAndFlush(hm.getSendMsg());
									ChannelFuture cfc = ctx
											.writeAndFlush(new DatagramPacket(hm.getSendMsg(), msg.sender())).sync();
									cfc.addListener(new ChannelFutureListener() {
										@Override
										public void operationComplete(ChannelFuture future) throws Exception {
											if (future.isSuccess()) {
												logger.info(Imei+"0X81-2:回写成功");
											} else {
												logger.info(Imei+"0X81-2:回写失败");
											}
										}
									});
									flag = cfc.isSuccess();
								}

							}
						}
					});
					if (!flag || heartbeat_registered_num > 1) {
						// 前两次发送响应失败或者设备端发送多次注册包，再进行一次注册响应回复。
						Thread.sleep(30);
						hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
						// ctx.writeAndFlush(hm.getSendMsg());
						ctx.writeAndFlush(new DatagramPacket(hm.getSendMsg(), msg.sender())).sync();
					}
					break;
				case (byte) 0x82:
					// 心跳包
					hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
					// ctx.writeAndFlush(hm.getSendMsg());
					ChannelFuture fxt = ctx.writeAndFlush(new DatagramPacket(hm.getSendMsg(), msg.sender())).sync();
					fxt.addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							if (future.isSuccess()) {
								logger.info(Imei+"0X82:回写成功");
							} else {
								logger.info(Imei+"0X82:回写失败");
							}
						}
					});
					break;
				case (byte) 0x83:
					// 数据包
					logger.info(Imei+":接受83数据包！");
					break;
				case (byte) 0x84:
					// 时间同步包
					hm.setSendMsg(ToDeviceClockMsg(hm.getImei(), type));
					// ctx.writeAndFlush(hm.getSendMsg());
					ChannelFuture fsj = ctx.writeAndFlush(new DatagramPacket(hm.getSendMsg(), msg.sender())).sync();
					fsj.addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							if (future.isSuccess()) {
								logger.info(Imei+"0X84:回写成功");
							} else {
								logger.info(Imei+"0X84:回写失败");
							}
						}
					});
					// 存储问题答案
					// redisUtils.lPush(msg.getImei()+Parameter.imei_info_downdata,JSON.toJSONString(msg.getSendMsg()));//存储所有updata数据
					break;
				case (byte) 0x85:
					// 正对与主动上报的数据回复
					ByteBuf Sendbuf = ToDeviceClockMsg(hm.getImei(), type);
					hm.setSendMsg(Sendbuf);
					// ByteBuf Sendbuf2 = hm.getSendMsg();
					DatagramPacket data = new DatagramPacket(Sendbuf, msg.sender());
					// ChannelFuture f = ctx.writeAndFlush(Sendbuf2);

					// 向客户端发送消息
					// 由于数据报的数据是以字符数组传的形式存储的，所以传转数据

					ChannelFuture f = ctx.writeAndFlush(data).sync();
					f.addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							StringBuilder sb = new StringBuilder("");
							if (future.isSuccess()) {
								logger.info(Imei+"0X85:回写成功");
							} else {
								logger.info(Imei+"0X85:回写失败");
							}
						}
					});

					// redisUtils.lPush(msg.getImei()+Parameter.imei_info_downdata,JSON.toJSONString(msg.getSendMsg()));//存储所有updata数据
					break;
				}

				// ReferenceCountUtil.release(recevieMsg);
				ctx.fireChannelRead(hm);
			} else {
				logger.info("验证帧头:0x80--不通过");
				// 验证不通过关闭channel
				// ctx.writeAndFlush(CallBackMessage.ERROR.duplicate());
				// ctx.close();
			}
		} catch (Exception e) {
			logger.error(ChannelID + e.getMessage());
		} finally {
			// ReferenceCountUtil.release(in);
			// logger.error(ChannelID+"ParseMessgaeHandlerfinally");
		}
	}

	@Override
	public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
		// TODO Auto-generated method stub
		super.channelRegistered(ctx);
		// System.out.println("创建连接");
		logger.info("创建UDP连接");
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
		// System.out.println("UDP通道已经连接");
		logger.info("UDP通道已经连接");
		// Channel incoming = ctx.channel();
		UDPConst.add(String.valueOf(UUID.randomUUID()), ctx.channel());
		channels.add(ctx.channel());
		// logger.info("client " + incoming.remoteAddress() + "上线");
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
		String channelId = UDPConst.getChannelIds(ctx.channel());

		if (!"".equals(channelId) && channelId.indexOf("_") == -1) {
			onoffLineAction(channelId, Parameter.offline_des, 0);// 记录下线
			onoffLineForCenter(channelId, 0);
			// 清掉redis中关于心跳记录
			// ImeiInfo imeiInfo =
			// JSON.parseObject(redisUtils.hmGet(Parameter.imei_info,channelId).toString(),
			// ImeiInfo.class);
			// imeiInfo.setHeartbeat_delay_num(0);
			// imeiInfo.setHeartbeat_registered_num(0);
			// redisUtils.hmSet(Parameter.imei_info, channelId,
			// JSON.toJSONString(imeiInfo));
		}
		// UDPConst.remove(ctx.channel());
		logger.info("UDP通道已经关闭");
		// channels.remove(ctx.channel());
		// 记录下线
		// logger.info("Disconnected client " + ctx.channel().remoteAddress() +
		// "掉线" + channelId);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (7)
		Channel incoming = ctx.channel();
		logger.info("client " + incoming.remoteAddress() + "异常" + cause.getMessage());
		// 当出现异常就关闭连接
		cause.printStackTrace();
		ctx.close();
	}

	/**
	 * 上线操作
	 * 
	 * @param imei
	 * @param channel
	 */
	private void onoffLineAction(String imei, String type, int status) {
		OnOffData onOff = new OnOffData();
		onOff.setImei(imei);
		onOff.setCreatetime(System.currentTimeMillis());
		onOff.setAction(type);
		redisUtils.lPush(imei + Parameter.on_off_info, JSON.toJSONString(onOff));// 存储所有updata数据
	}

	/**
	 * 上线下线操作
	 * 
	 * @param imei
	 * @param channel
	 */
	@SuppressWarnings("unused")
	private void onoffLineForCenter(String imei, int status) {
		Terminals terminal = new Terminals();
		terminal.setImei(imei);
		terminal.setOnlinestatus(status);
		// ts.updateTerminalOnlineStatus(terminal);
	}

	public String getTimeNow() {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return df.format(new Date());
	}

	/**
	 * 获取超时时间
	 * 
	 * @param timeNow
	 */
	public String getTimeOut(String timeNow) {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Calendar cal = new GregorianCalendar();

		try {
			cal.setTime(df.parse(timeNow));
			cal.add(Calendar.SECOND, Parameter.outtime_hear);

		} catch (ParseException e) {
			e.printStackTrace();
		}
		return df.format(cal.getTime());
	}

	/**
	 * 比较超时时间，是否超时
	 */
	public int CompareOutTime(String timeNow, String timeAfter, SimpleDateFormat timestampFormat) {
		if (timeNow.equals(timeAfter)) // same
			return 1;
		try {
			Date timesN = timestampFormat.parse(timeNow);
			Date timesA = timestampFormat.parse(timeAfter);
			if (timesA.after(timesN)) // time 1 is newer
				return 1;
			else
				return -1;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return 1;
	}

	/**
	 * 
	 * @param imei
	 * @param type
	 * @return
	 */
	public ByteBuf ToDeviceClockMsg(String imei, byte type) {
		SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");
		String date = df.format(new Date());
		byte[] temp2_ = ConvertUtils.getDateFromStr(date, 6);
		byte[] msg = new byte[22 + temp2_.length];
		msg[0] = (byte) 0x80;
		msg[1] = 0x28;
		msg[2] = 0;
		msg[3] = type;

		byte[] temp = imei.getBytes();
		System.arraycopy(temp, 0, msg, 4, 15);// 类型、imei

		// byte[] temp = ConvertUtils.getByte(Integer.parseInt(imei), 15);
		// System.arraycopy(temp, 0, msg, 4, 15);
		System.arraycopy(temp2_, 0, msg, 19, 6);
		msg[msg.length - 3] = ConvertUtils.getTotalForByte(msg);
		msg[msg.length - 2] = 0x0d;
		msg[msg.length - 1] = 0x0a;
		return Unpooled.wrappedBuffer(msg);
	}

	public ByteBuf GetSuccessSignal(String imei, byte type, boolean suc) {
		byte[] msg = new byte[23];
		msg[0] = (byte) 0x80;
		msg[1] = 0x23;
		msg[2] = 0;
		msg[3] = type;
		byte[] temp = imei.getBytes();
		System.arraycopy(temp, 0, msg, 4, 15);// 类型、imei
		msg[19] = (byte) (suc ? 0x73 : 0x74);// 应答状态
		msg[20] = ConvertUtils.getTotalForByte(msg);// 校验和
		msg[21] = 0x0d;
		msg[22] = 0x0a;
		return Unpooled.wrappedBuffer(msg);
	}

}
